libobs_wrapper\display\window_manager/
position_trait.rs

1use windows::Win32::{
2    Foundation::HWND,
3    Graphics::Gdi::{RedrawWindow, RDW_ERASE, RDW_INVALIDATE},
4    UI::WindowsAndMessaging::{
5        SetWindowPos, HWND_BOTTOM, SWP_NOACTIVATE, SWP_NOCOPYBITS, SWP_NOSIZE, SWP_NOZORDER,
6        SWP_SHOWWINDOW,
7    },
8};
9
10use crate::{display::ObsDisplayRef, run_with_obs, rw_lock_blocking_read};
11
12#[cfg_attr(not(feature = "blocking"), async_trait::async_trait)]
13pub trait WindowPositionTrait {
14    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
15    async fn set_render_at_bottom(&self, render_at_bottom: bool);
16    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
17    async fn get_render_at_bottom(&self) -> bool;
18    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
19    async fn set_pos(&self, x: i32, y: i32) -> windows::core::Result<()>;
20    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
21    async fn set_size(&self, width: u32, height: u32) -> anyhow::Result<()>;
22    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
23    async fn set_scale(&self, scale: f32);
24
25    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
26    async fn get_pos(&self) -> (i32, i32);
27    fn get_pos_blocking(&self) -> (i32, i32);
28
29    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
30    async fn get_size(&self) -> (u32, u32);
31    fn get_size_blocking(&self) -> (u32, u32);
32
33    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
34    async fn get_scale(&self) -> f32;
35}
36
37#[cfg_attr(not(feature = "blocking"), async_trait::async_trait)]
38impl WindowPositionTrait for ObsDisplayRef {
39    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
40    async fn set_render_at_bottom(&self, render_at_bottom: bool) {
41        log::trace!("Set render bottom");
42        self.manager.write().await.render_at_bottom = render_at_bottom;
43    }
44
45    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
46    async fn get_render_at_bottom(&self) -> bool {
47        self.manager.read().await.render_at_bottom
48    }
49
50    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
51    async fn set_pos(&self, x: i32, y: i32) -> windows::core::Result<()> {
52        log::trace!("Set pos {x} {y}");
53        let mut m = self.manager.write().await;
54
55        assert!(
56            m.obs_display.is_some(),
57            "Invalid state. The display should have been created and set, but it wasn't."
58        );
59
60        let insert_after = if m.render_at_bottom {
61            HWND_BOTTOM
62        } else {
63            HWND::default()
64        };
65
66        m.x = x;
67        m.y = y;
68
69        unsafe {
70            let flags = SWP_NOCOPYBITS | SWP_NOSIZE | SWP_NOACTIVATE;
71            // Just use dummy values as size is not changed
72            SetWindowPos(
73                m.hwnd.0,
74                Some(insert_after),
75                x,
76                y,
77                1 as i32,
78                1 as i32,
79                flags,
80            )?;
81        }
82
83        Ok(())
84    }
85
86    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
87    async fn get_pos(&self) -> (i32, i32) {
88        let m = self.manager.read().await;
89        (m.x, m.y)
90    }
91    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
92    fn get_pos_blocking(&self) -> (i32, i32) {
93        let m = rw_lock_blocking_read!(self.manager);
94        (m.x, m.y)
95    }
96
97    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
98    async fn get_size(&self) -> (u32, u32) {
99        let m = self.manager.read().await;
100        (m.width, m.height)
101    }
102
103    fn get_size_blocking(&self) -> (u32, u32) {
104        let m = rw_lock_blocking_read!(self.manager);
105        (m.width, m.height)
106    }
107
108    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
109    async fn set_size(&self, width: u32, height: u32) -> anyhow::Result<()> {
110        log::trace!("Set size {width} {height}");
111        let mut m = self.manager.write().await;
112        assert!(
113            m.obs_display.is_some(),
114            "Invalid state. The display should have been created and set, but it wasn't."
115        );
116
117        m.width = width;
118        m.height = height;
119
120        let pointer = m.obs_display.as_ref().unwrap().clone();
121        unsafe {
122            SetWindowPos(
123                m.hwnd.0,
124                None,
125                m.x,
126                m.y,
127                width as i32,
128                height as i32,
129                SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW,
130            )?;
131
132            let _ = RedrawWindow(Some(m.hwnd.0), None, None, RDW_ERASE | RDW_INVALIDATE);
133        }
134
135        run_with_obs!(self.runtime, (pointer), move || unsafe {
136            libobs::obs_display_resize(pointer, width, height);
137        }).await?;
138        Ok(())
139    }
140
141    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
142    async fn set_scale(&self, scale: f32) {
143        log::trace!("Set scale {scale}");
144        self.manager.write().await.scale = scale;
145    }
146
147    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
148    async fn get_scale(&self) -> f32 {
149        self.manager.read().await.scale
150    }
151}